package com.sh.d1.gameserver;

import com.google.protobuf.Message;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 游戏消息解码器
 */
public class GameMsgDecoder extends ChannelInboundHandlerAdapter {
    /**
     * 日志对象
     */
    static private final Logger LOGGER = LoggerFactory.getLogger(GameMsgDecoder.class);

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msgObj) throws Exception {
        if (!(msgObj instanceof BinaryWebSocketFrame)) {
            return;
        }

        // WebSocket 二进制消息会通过 HttpServerCodec 解码成 BinaryWebSocketFrame
        BinaryWebSocketFrame inputFrame = (BinaryWebSocketFrame) msgObj;
        ByteBuf byteBuf = inputFrame.content();

        byteBuf.readShort(); // 读取消息长度
        int gatewayServerId = byteBuf.readInt(); // 读取网关服务器 Id
        int remoteSessionId = byteBuf.readInt(); // 读取远程主机上的会话 Id
        int fromUserId = byteBuf.readInt(); // 读取来自用户 Id
        int msgCode = byteBuf.readShort(); // 读取消息编号

        LOGGER.info(
            "收到网关服务器消息, remoteSessionId = {}, msgCode = {}",
            remoteSessionId,
            msgCode
        );

        // 根据消息编号获取构建器
        Message.Builder msgBuilder = GameMsgRecognizer.getMsgBuilderByMsgCode(msgCode);
        if (null == msgBuilder) {
            LOGGER.error("无法识别的消息, msgCode = {}", msgCode);
            return;
        }

        byte[] msgBody = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(msgBody);

        // 解析字节数组
        msgBuilder.clear();
        msgBuilder.mergeFrom(msgBody);

        // 构建指令并向下处理
        Message newCmd = msgBuilder.build();

        if (null != newCmd) {
            // 创建内部服务器消息
            InternalServerMsg innerMsg = new InternalServerMsg()
                .setGatewayServerId(gatewayServerId)
                .setRemoteSessionId(remoteSessionId)
                .setFromUserId(fromUserId)
                .setMsgCode(msgCode)
                .setMsgObj(newCmd);

            // XXX 注意: fire 之后,
            // 后续执行的 ChannelHandler 所处理的消息就变成 InternalServerMsg 了...
            ctx.fireChannelRead(innerMsg);
        }
    }
}
